
package analisadorlexico;

/**
 *
 * @author Anderson Vinicius
 */
public class ProcessadorLexico {

    /**
     * Caracteres especiais reconhecidos pela linguagem
     */
    private static final String CARACTERES_ESPECIAIS = "_,;.:()=<>+-*/{} \n\t\r";

    private Tabela<TokenType> tabela = new Tabela<TokenType>();
    private Tabela<ErrorType> erros = new Tabela<ErrorType>();
    
    int size;
    int contador, lineNumber;
    char charAtual;
    String program, temp;
        
    public void run(String program_) {
        program = program_;
        size = program.length();
        contador = 0; lineNumber = 1;
        temp = "";
        
        while(contador < size) {
            charAtual = program.charAt(contador);
            
            if(isSimbolValid(charAtual)) {
                
                if(charAtual == ' ' || charAtual == '\t' || charAtual == '\r') {
                    contador++;
                } 
                else if(Character.isLetter(charAtual)) {
                    temp += charAtual;
                    contador++;
                    automatoD();
                }
                else if(Character.isDigit(charAtual)) {
                    temp += charAtual;
                    contador++;
                    automatoG();
                }
                else if (charAtual == '+' || charAtual == '-' || charAtual == '/' || charAtual == '*'){ //vai para o estado F
            		automatoF();
            	} else if (charAtual == '(' || charAtual == ')' || charAtual == '.' || charAtual == ';' || charAtual == ','){//vai para o estado Y
            		automatoY();
            	} else if (charAtual == '>'){//vai para o T
                    temp += charAtual;
                    contador++;
                    automatoT();
            	} else if (charAtual == '<'){//vai para o K
                    temp += charAtual;
                    contador++;
                    automatoK();
            	} else if (charAtual == ':'){//vai para o W
                    temp += charAtual;
                    contador++;
                    automatoW();
            	} else if(charAtual == '\n') { // vai para o estado I
                    lineNumber++;
                    contador++;
                    automatoI();
                } else if(charAtual == '{') { // vai para o estado B
                    contador++;
                    automatoB();
                } else if(charAtual == '}') {
                    erros.add(""+charAtual, ErrorType.ERRO_COMENTARIO, lineNumber);
                    contador++;
                }
            } 
            else { // isSimbolValid(charAtual)? -> false
            	erros.add(""+charAtual, ErrorType.SIMBOLO_NAO_VALIDO, lineNumber);
                contador++;
            }
        }
    }

    private void automatoB() {
        while(contador < size) {
            charAtual = program.charAt(contador);
            
            if(charAtual != '\n' && charAtual != '}') { //continua no estado B
                contador++;
            } else if(charAtual == '}') { // vai para o estado E
                contador++;
                return; // vai para o estado A
            } else if(charAtual == '\n') { // vai para o estado C
                contador++;
                lineNumber++;
                automatoC();
                if(charAtual == '}') {
                    return; // vai para o estado A
                }
            }
        }
        /* Este trecho sera executado apenas se o final do arquivo
         foi atingido */
        if(contador == size) { // terminou o arquivo e nao fechou o comentario
            erros.add(""+charAtual, ErrorType.ERRO_COMENTARIO, lineNumber);
        }
    }

    private void automatoC() {
        while(contador < size) {
            charAtual = program.charAt(contador);
            if(charAtual == '\n') { // continua no estado C
                contador++;
                lineNumber++;
            } else if(charAtual == '}') { // vai para o estado E
                contador++;
                return; // vai para o estado A
            } else if(charAtual != '\n' || charAtual != '}') {
                contador++;
                return; // vai para o estado B
            }
        }
        if(contador == size && charAtual != '}') {
            erros.add(""+charAtual, ErrorType.ERRO_COMENTARIO, lineNumber);
        }
    }



    /*
     * Automato que reconhece o caracter de passar para a proxima linha
     * e que incrementa o contador de linhas
     */
    private void automatoI() {
        while(contador < size) {
            charAtual = program.charAt(contador);
            if(charAtual == '\n') { // continua no estado I
                lineNumber++;
                contador++;
            }
            else { // vai para o estado A
                return;
            }
        }
    }
    
    private void automatoW() {
        if(contador < size) {
            charAtual = program.charAt(contador);
            if (charAtual == '=') {//vai para o X
                temp += charAtual;
                automatoX();
            } else{
                tabela.add(temp, TokenType.DELIMITADOR, lineNumber);
                temp = "";
                //ira voltar para o inicio do automato - sem fazer contador++. Pois o caracter lido, sera testado novamente.
            }
        }
        else { // chegou ao fim do programa
            tabela.add(temp, TokenType.DELIMITADOR, lineNumber);
            temp = "";
        }
    }

    private void automatoX() {
        tabela.add(temp, TokenType.ATRIBUICAO, lineNumber);
        temp = "";
        contador++;
    }
    private void automatoK() {
        if(contador < size) {
            charAtual = program.charAt(contador);
            if (charAtual == '=' || charAtual == '>'){//vai para o L
                temp += charAtual;
                automatoL();
            }else{
                tabela.add(temp, TokenType.OPERADOR_RELACIONAL, lineNumber);
                temp = "";
                //ira voltar para o inicio do automato - sem fazer contador++. Pois o caracter lido, sera testado novamente.
            }
        }
        else { // chegou ao fim do programa
            tabela.add(temp, TokenType.OPERADOR_RELACIONAL, lineNumber);
            temp = "";
        }
    }

    private void automatoL() {
        tabela.add(temp, TokenType.OPERADOR_RELACIONAL, lineNumber);
        temp = "";
        contador++;
    }

    private void automatoT() {
        if(contador < size) {
            charAtual = program.charAt(contador);
            if (charAtual == '='){//vai para o U
                temp += charAtual;
                automatoU();
            } else{
                tabela.add(temp, TokenType.OPERADOR_RELACIONAL, lineNumber);
                temp = "";
                //ira voltar para o inicio do automato - sem fazer contador++. Pois o caracter lido, sera testado novamente.
            }
        }
        else { // chegou ao fim do programa
            tabela.add(temp, TokenType.OPERADOR_RELACIONAL, lineNumber);
            temp = "";
        }
    }

    private void automatoU() {
        tabela.add(temp, TokenType.OPERADOR_RELACIONAL, lineNumber);
        temp = "";
        contador++;
    }
    
    /*
     * Automato que reconhece delimitadores
     */
    private void automatoY() {
        tabela.add(""+charAtual, TokenType.DELIMITADOR, lineNumber); //estou fazendo de acordo com o automato, como se nao existisse (( juntos.
        contador++;
    }

    /*
     * Automato que reconhece operadores aditivos e multiplicativos
     * e o operador relacional de igualdade
     */
    private void automatoF() {
        if(charAtual == '+'){
            tabela.add(""+charAtual, TokenType.OPERADOR_ADITIVO, lineNumber);
            contador++;
        }
        if(charAtual == '-'){
            tabela.add(""+charAtual, TokenType.OPERADOR_ADITIVO, lineNumber);
            contador++;
        }
        if(charAtual == '/'){
            tabela.add(""+charAtual, TokenType.OPERADOR_MULTIPLICATIVO, lineNumber);
            contador++;
        }
        if(charAtual == '*'){
            tabela.add(""+charAtual, TokenType.OPERADOR_MULTIPLICATIVO, lineNumber);
            contador++;
        }
        if (charAtual == '='){ //vai para o F
            tabela.add(""+charAtual, TokenType.OPERADOR_RELACIONAL, lineNumber);
            contador++;
        }
    }

    /**
     * Automato que reconhece numeros inteiros ou reais
     */
    private void automatoG() {
        while(contador < size) {
            charAtual = program.charAt(contador);
            if(Character.isDigit(charAtual)) { // continua no estado G
                temp += charAtual;
                contador++;
            }
            else if(charAtual == '.') { // vai para o estado H
                temp += charAtual;
                contador++;
                automatoJ();
                return; // volta para o estado A
            }
            else { // simbolo nao reconhecido pelo estado G
                tabela.add(temp, TokenType.NUMERO_INTEIRO, lineNumber);
                temp = "";
                return; // volta para o estado A
            }
        }

        /* Este trecho sera executado apenas se o final do arquivo
         foi atingido */
        if(contador == size) {
            tabela.add(temp, TokenType.NUMERO_INTEIRO, lineNumber);
            temp = "";
        }
    }

    /**
     * Automato para estado nao final que reconhece o ponto de numeros reais
     */
    private void automatoJ() {
        if(contador < size) {
            charAtual = program.charAt(contador);
            if(Character.isDigit(charAtual)) { // vai para o estado H
                temp += charAtual;
                contador++;
                automatoH();
                return; // volta para o estado G
            }
            else {
                contador--;
                temp = temp.substring(0, temp.length()-1);
                tabela.add(temp, TokenType.NUMERO_INTEIRO, lineNumber);
                temp = "";
                return; // volta para o estado G
            }
        }
        else {
            contador--;
            temp = temp.substring(0, temp.length()-1);
            tabela.add(temp, TokenType.NUMERO_INTEIRO, lineNumber);
            temp = "";
            return; // volta para o estado G
        }
    }

    /*
     * Automato que reconhece a mantissa de um numero real
     */
    private void automatoH() {
        while(contador < size) {
            charAtual = program.charAt(contador);
            if(Character.isDigit(charAtual)) { // continua no estado H
                temp += charAtual;
                contador++;
            }
            else { // simbolo nao reconhecido pelo estado H
                tabela.add(temp, TokenType.NUMERO_REAL, lineNumber);
                temp = "";
                return;
            }
        }

        /* Este trecho sera executado apenas se o final do arquivo
         foi atingido */
        if(contador == size) {
            tabela.add(temp, TokenType.NUMERO_REAL, lineNumber);
            temp = "";
        }
    }

    /*
     * Automatico que reconhece variaveis
     */
    private void automatoD() {
        while(contador < size) {
            charAtual = program.charAt(contador);
            if(Character.isLetterOrDigit(charAtual) || charAtual == '_') {
                temp += charAtual;
                contador++;
            }
            else { // simbolo nao reconhecido pelo estado D
                if(TokenType.isPalavraReservada(temp)) {
                    if(temp.equalsIgnoreCase("and")) {
                        tabela.add(temp, TokenType.OPERADOR_MULTIPLICATIVO, lineNumber);
                    } else if(temp.equalsIgnoreCase("or")) {
                        tabela.add(temp, TokenType.OPERADOR_ADITIVO, lineNumber);
                    } else {
                        tabela.add(temp, TokenType.PALAVRA_RESERVADA, lineNumber);
                    }
                    temp = "";
                }
                else {
                    tabela.add(temp, TokenType.IDENTIFICADOR, lineNumber);
                    temp = "";
                }
                return; // volta para o estado A
            }
        }

        /* Este trecho sera executado apenas se o final do arquivo
         foi atingido */
        if(contador == size) {
            if(TokenType.isPalavraReservada(temp)) {
                tabela.add(temp, TokenType.PALAVRA_RESERVADA, lineNumber);
                temp = "";
            }
            else {
                tabela.add(temp, TokenType.IDENTIFICADOR, lineNumber);
                temp = "";
            }
        }
    }
    
    /**
     * Verifica se eh um token valido da linguagem
     * @param token
     * @return
     */
    public boolean isSimbolValid(char simbol) {
        if(Character.isLetterOrDigit(simbol) || isCaracterEspecial(simbol)) {
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * Verifica se eh um dos caracteres especiais da linguagem
     * @param token
     * @return
     */
    public static boolean isCaracterEspecial(char simbol) {
        if(CARACTERES_ESPECIAIS.indexOf(simbol) > 0) {
            return true;
        }
        else {
            return false;
        }
    }

    public Tabela<ErrorType> getErros() {
        return erros;
    }

    public Tabela<TokenType> getTabela() {
        return tabela;
    }

    
}